/* 
* File:   Joystick.cpp
* Author: Ben
* 
* Created on May 1, 2013, 7:32 PM
*/

#include "DIJoystick.h"
#include "Logger.h"
#include "Error.h"
#include <iostream>

namespace controllers{

	Joystick::Joystick(const LPDIRECTINPUT8 di, const LPCDIDEVICEINSTANCE dev): 
		name(dev->tszProductName),
		guid(dev->guidInstance),
		directInput(di),
		type(Joystick::Type(dev->dwDevType)),
		acquired(false) {

	}

	Joystick::Joystick(Joystick&& other): 
		name(other.name),
		guid(other.guid),
		directInput(other.directInput),
		controller(other.controller),
		type(other.type),
		acquired(other.acquired) {
			other.directInput = NULL;
			other.controller = NULL;
	}

	Joystick::Joystick(const Joystick& orig):
		name(orig.name),
		guid(orig.guid),
		directInput(orig.directInput),
		controller(orig.controller),
		type(orig.type),
		acquired(orig.acquired) {}

	Joystick::~Joystick() {
		if(acquired && controller != NULL) {
			controller->Release();
		}
	}

	const std::wstring& Joystick::getName() const {
		return name;
	}

	Joystick& Joystick::operator=(Joystick&& other){
		name = other.name;
		guid = other.guid;
		delete controller;
		controller = other.controller;
		other.controller = NULL;
		type = other.type;

		return *this;
	}

	bool Joystick::poll() {
		HRESULT resPoll = controller->Poll();
		HRESULT resState = controller->GetDeviceState(sizeof(JoystickData), &data);
		//std::wcout << data.yAxis << std::endl;
		return SUCCEEDED(resPoll) && SUCCEEDED(resState);
	}


	void Joystick::create(){
		// Create device
		directInput->CreateDevice(guid, &controller, NULL);

		// Set data format
		if(FAILED(controller->SetDataFormat(&kDataFormat))){
			logger::Logger::getLogger().log(logger::Error::kSetJoystickDataFormatFailed);
		}

		// Setup x axis range property object
		DIPROPRANGE range;
		range.diph.dwSize = sizeof(DIPROPRANGE);
		range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
		range.diph.dwHow = DIPH_BYOFFSET;
		range.diph.dwObj = DIJOFS_X;
		range.lMin = -kRange;
		range.lMax = kRange;

		// Set x axis range
		if(FAILED(controller->SetProperty(DIPROP_RANGE, &range.diph))){
			logger::Logger::getLogger().log(logger::Error::kSetJoystickRangeFailed);
		}

		// Set y axis range
		range.diph.dwObj = DIJOFS_Y;
		if(FAILED(controller->SetProperty(DIPROP_RANGE, &range.diph))){
			logger::Logger::getLogger().log(logger::Error::kSetJoystickRangeFailed);
		}

		DIPROPDWORD axisMode;
		axisMode.diph.dwHeaderSize = sizeof(DIPROPHEADER);
		axisMode.diph.dwSize = sizeof(DIPROPDWORD);
		axisMode.diph.dwHow = DIPH_DEVICE;
		axisMode.diph.dwObj = 0;
		axisMode.dwData = DIPROPAXISMODE_ABS;

		if(FAILED(controller->SetProperty(DIPROP_AXISMODE, &axisMode.diph))){
		}
	}

	void Joystick::acquire(){
		if(!SUCCEEDED(controller->Acquire())) {
			std::wcout << L"Unable to acquire device";
		}
	}

	Joystick::Type Joystick::getType() const {
		return type;
	}

	float Joystick::getX() const {
		return (float)(data.xAxis / ((double)kRange));
	}

	float Joystick::getY() const {
		return (float)(data.yAxis / ((double)kRange));
	}

	bool Joystick::getTrigger() const {
		return data.trigger == kButtonPressed;
	}

	bool Joystick::getRightClick() const {
		return data.rightClick == kButtonPressed;
	}

	bool Joystick::getScrollUp() const {
		return data.scrollUp == kButtonPressed;
	}

	bool Joystick::getScrollDown() const {
		return data.scrollDown == kButtonPressed;
	}

	DIOBJECTDATAFORMAT Joystick::kObjectDataFormat[] = {
		{&GUID_XAxis, FIELD_OFFSET(JoystickData, xAxis), DIDFT_ABSAXIS | DIDFT_ANYINSTANCE, 0},
		{&GUID_YAxis, FIELD_OFFSET(JoystickData, yAxis), DIDFT_ABSAXIS | DIDFT_ANYINSTANCE, 0},
		{&GUID_Button, FIELD_OFFSET(JoystickData, trigger), DIDFT_BUTTON | DIDFT_MAKEINSTANCE(0), 0},
		{&GUID_Button, FIELD_OFFSET(JoystickData, rightClick), DIDFT_BUTTON | DIDFT_MAKEINSTANCE(4), 0},
		{&GUID_Button, FIELD_OFFSET(JoystickData, scrollUp), DIDFT_BUTTON | DIDFT_MAKEINSTANCE(2), 0},
		{&GUID_Button, FIELD_OFFSET(JoystickData, scrollDown), DIDFT_BUTTON | DIDFT_MAKEINSTANCE(1), 0}
	};

	const DIDATAFORMAT Joystick::kDataFormat = {
		sizeof(DIDATAFORMAT),
		sizeof(DIOBJECTDATAFORMAT),
		DIDFT_ABSAXIS,
		sizeof(JoystickData),
		(sizeof(kObjectDataFormat) / sizeof(kObjectDataFormat[0])),
		kObjectDataFormat,
	};

	// Class Joystick::Type

	Joystick::Type::Type(DWORD type):type(type) {}

	Joystick::Type::Type(const Joystick::Type &orig) : type(orig.type) {}

	bool Joystick::Type::operator==(Joystick::Type& rhs) const{
		return ((type & rhs.type) == rhs.type);
	}

	Joystick::Type Joystick::Type::kJoystick = Type(DI8DEVTYPE_JOYSTICK);
	Joystick::Type Joystick::Type::kGamepad = Type(DI8DEVTYPE_GAMEPAD);
	Joystick::Type Joystick::Type::kKeyboard = Type(DI8DEVTYPE_KEYBOARD);
	Joystick::Type Joystick::Type::kUnknown = Type(-1);
}